Ext.util.MixedCollection = function( allowFunctions, keyFn )
{
	this.items = [];
	this.map = {};
	this.keys = [];
	this.length = 0;
	this.addEvents('clear', 'add', 'replace', 'remove', 'sort');
	this.allowFunctions = allowFunctions === true;
	if( keyFn )
	{
		this.getKey = keyFn;
	}
	Ext.util.MixedCollection.superclass.constructor.call(this);
};

Ext.extend(Ext.util.MixedCollection, Ext.util.Observable,
{

	// 是否允许在mixedCollection里加入function引用
	allowFunctions: false,

	add: function( key, o )
	{
		// 如果只有一个参数，m.add('aaaaa');
		// 那么o = aaaaa; key = undefined;
		// items-->[aaaaaa],key-->[undefined]
		if( arguments.length == 1 )
		{
			o = arguments[0];
			key = this.getKey(o);
		}

		// 如果key不是undefined并且不是null，那么。。。
		if( typeof key != 'undefined' && key !== null )
		{
			var old = this.map[key];
			if( typeof old != 'undefined' )
			{
				// 将原有的key的value值换成o
				return this.replace(key, o);
			}

			this.map[key] = o;
		}
		this.length++;
		this.items.push(o);
		this.keys.push(key);

		// 触发add事件，参数为(最后添加的这个元素的索引，添加的值，添加的key)
		this.fireEvent('add', this.length - 1, o, key);
		return o;
	},

	getKey: function( o )
	{
		return o.id;
	},

	replace: function( key, o )
	{

		// 是否为一个参数，若是：取o的id为key
		if( arguments.length == 1 )
		{
			o = arguments[0];
			key = this.getKey(o);
		}

		var old = this.map[key];

		// 若是add调用的，不会进入这个if
		if( typeof key == 'undefined' || key === null || typeof old == 'undefined' )
		{
			return this.add(key, o);
		}
		// 找到key的索引
		var index = this.indexOfKey(key);

		// 覆盖之
		this.items[index] = o;
		this.map[key] = o;

		// 触发replace事件
		this.fireEvent('replace', key, old, o);
		return o;
	},

	// 添加一系列的值
	addAll: function( objs )
	{
		// 如果参数大于1个
		if( arguments.length > 1 || Ext.isArray(objs) )
		{
			var args = arguments.length > 1 ? arguments : objs;
			for( var i = 0, len = args.length; i < len; i++ )
			{
				this.add(args[i]);
			}
		}
		else
		{
			for( var key in objs )
			{
				if( this.allowFunctions || typeof objs[key] != 'function' )
				{
					this.add(key, objs[key]);
				}
			}
		}
	},

	// 遍历，跟JQuery的写法貌似很像嘛
	each: function( fn, scope )
	{
		var items = [].concat(this.items); // each safe for removal
		for( var i = 0, len = items.length; i < len; i++ )
		{
			// 如果返回false，那么就直接跳出循环
			if( fn.call(scope || items[i], items[i], i, len) === false )
			{
				break;
			}
		}
	},

	// 遍历key
	eachKey: function( fn, scope )
	{
		for( var i = 0, len = this.keys.length; i < len; i++ )
		{
			fn.call(scope || window, this.keys[i], this.items[i], i, len);
		}
	},

	// 通过fn来找符合条件的item
	find: function( fn, scope )
	{
		for( var i = 0, len = this.items.length; i < len; i++ )
		{
			if( fn.call(scope || window, this.items[i], this.keys[i]) )
			{
				return this.items[i];
			}
		}
		return null;
	},

	// 插入
	insert: function( index, key, o )
	{
		if( arguments.length == 2 )
		{
			o = arguments[1];
			key = this.getKey(o);
		}

		// 如果包含当前key，那么就删除之
		if( this.containsKey(key) )
		{
			// 停止当前事件触发
			this.suspendEvents();
			this.removeKey(key);
			// 恢复事件触发
			this.resumeEvents();
		}

		// 若果index大于length，就直接调用add方法
		if( index >= this.length )
		{
			return this.add(key, o);
		}
		this.length++;

		// 正在的插入方法
		this.items.splice(index, 0, o);
		if( typeof key != 'undefined' && key !== null )
		{
			this.map[key] = o;
		}
		this.keys.splice(index, 0, key);

		// 触发add事件，为啥不触发insert事件？
		this.fireEvent('add', index, o, key);
		return o;
	},

	// 移除一个
	remove: function( o )
	{
		return this.removeAt(this.indexOf(o));
	},

	// 移除，传入索引
	removeAt: function( index )
	{
		if( index < this.length && index >= 0 )
		{
			this.length--;
			var o = this.items[index];
			this.items.splice(index, 1);
			var key = this.keys[index];
			if( typeof key != 'undefined' )
			{
				delete this.map[key];
			}
			this.keys.splice(index, 1);
			// fire remove event
			this.fireEvent('remove', o, key);
			return o;
		}
		return false;
	},

	// 通过key索引
	removeKey: function( key )
	{
		return this.removeAt(this.indexOfKey(key));
	},

	// 获得长度
	getCount: function()
	{
		return this.length;
	},

	// item索引
	indexOf: function( o )
	{
		return this.items.indexOf(o);
	},

	// key索引
	indexOfKey: function( key )
	{
		return this.keys.indexOf(key);
	},

	// 通过key得到value
	item: function( key )
	{
		// 如果this.map[key]中有值，那么返回，如果没有并且key是数字，那么就返回items[key]
		var mk = this.map[key], item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;

		// 检查是否是函数，如果allowFunctions为false并且为函数，则返回null
		return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
	},

	// 通过index，获得item
	itemAt: function( index )
	{
		return this.items[index];
	},

	// 通过key，获取value
	key: function( key )
	{
		return this.map[key];
	},

	// 是否包含传入的对象
	contains: function( o )
	{
		return this.indexOf(o) != -1;
	},

	// 是否包含传入的key
	containsKey: function( key )
	{
		return typeof this.map[key] != 'undefined';
	},

	// 清空所有
	clear: function()
	{
		this.length = 0;
		this.items = [];
		this.keys = [];
		this.map = {};
		// fire clear event
		this.fireEvent('clear');
	},

	// 得到第一个
	first: function()
	{
		return this.items[0];
	},

	// 得到最后一个
	last: function()
	{
		return this.items[this.length - 1];
	},

	/**
	 * 内部排序方法
	 * 
	 * @param {} property
	 * @param {} dir
	 * @param {} fn
	 */
	_sort: function( property, dir, fn )
	{
		var i, len, dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

		// this is a temporary array used to apply the sorting function
		c = [], keys = this.keys, items = this.items;

		// default to a simple sorter function if one is not provided
		fn = fn || function( a, b )
		{
			return a - b;
		};

		// copy all the items into a temporary array, which we will sort
		for( i = 0, len = items.length; i < len; i++ )
		{
			// 这是替换push的好方法？
			c[c.length] =
			{
				key: keys[i],
				value: items[i],
				index: i
			};
		}

		// sort the temporary array
		c.sort(function( a, b )
		{
			var v = fn(a[property], b[property]) * dsc;
			if( v === 0 )
			{
				v = (a.index < b.index ? -1 : 1);
			}
			return v;
		});

		// copy the temporary array back into the main this.items and this.keys objects
		for( i = 0, len = c.length; i < len; i++ )
		{
			items[i] = c[i].value;
			keys[i] = c[i].key;
		}

		// fire sort event
		this.fireEvent('sort', this);
	},

	/**
	 * 排序
	 * 
	 * @param {} dir 方向
	 * @param {} fn 排序函数
	 */
	sort: function( dir, fn )
	{
		this._sort('value', dir, fn);
	},

	// 根据从旧索引到新索引的映射关系，对每个项目进行重新排序。 在内部，这仅仅被当作一次排序。无论重新排序是否发生都会触发 'sort'事件。
	reorder: function( mapping )
	{
		this.suspendEvents();

		var items = this.items, index = 0, length = items.length, order = [], remaining = [], oldIndex;

		// object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
		for( oldIndex in mapping )
		{
			order[mapping[oldIndex]] = items[oldIndex];
		}

		for( index = 0; index < length; index++ )
		{
			if( mapping[index] == undefined )
			{
				remaining.push(items[index]);
			}
		}

		for( index = 0; index < length; index++ )
		{
			if( order[index] == undefined )
			{
				order[index] = remaining.shift();
			}
		}

		this.clear();
		this.addAll(order);

		this.resumeEvents();
		this.fireEvent('sort', this);
	},

	// 根据key进行排序
	keySort: function( dir, fn )
	{
		this._sort('key', dir, fn || function( a, b )
		{
			var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
			return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
		});
	},

	// 返回一个数组
	getRange: function( start, end )
	{
		var items = this.items;
		if( items.length < 1 )
		{
			return [];
		}
		start = start || 0;
		end = Math.min(typeof end == 'undefined' ? this.length - 1 : end, this.length - 1);
		var i, r = [];

		// 防止end比start小的情况，作为一个成熟的框架，这是必须的
		if( start <= end )
		{
			for( i = start; i <= end; i++ )
			{
				// 新颖的写法
				r[r.length] = items[i];
			}
		}
		else
		{
			for( i = start; i >= end; i-- )
			{
				r[r.length] = items[i];
			}
		}
		return r;
	},

	/**
	 * 过滤
	 * 
	 * @param {} property 要过滤的属性
	 * @param {} value
	 * @param {} anyMatch
	 * @param {} caseSensitive
	 * @return {}
	 */
	filter: function( property, value, anyMatch, caseSensitive )
	{
		if( Ext.isEmpty(value, false) )
		{
			return this.clone();
		}
		// 得到正则表达式
		value = this.createValueMatcher(value, anyMatch, caseSensitive);
		// 转交给filterBy
		return this.filterBy(function( o )
		{
			return o && value.test(o[property]);
		});
	},

	/**
	 * 通过函数过滤
	 * 
	 * @param {} fn
	 * @param {} scope
	 * @return {new Ext.util.MixedCollection}
	 */
	filterBy: function( fn, scope )
	{
		var r = new Ext.util.MixedCollection();
		r.getKey = this.getKey;
		var k = this.keys, it = this.items;
		for( var i = 0, len = it.length; i < len; i++ )
		{
			if( fn.call(scope || this, it[i], k[i]) )
			{
				r.add(k[i], it[i]);
			}
		}
		return r;
	},

	/**
	 * 通过属性来获取索引
	 * 
	 * @param {} property
	 * @param {} value
	 * @param {} start
	 * @param {} anyMatch
	 * @param {} caseSensitive
	 * @return {}
	 */
	findIndex: function( property, value, start, anyMatch, caseSensitive )
	{
		if( Ext.isEmpty(value, false) )
		{
			return -1;
		}
		value = this.createValueMatcher(value, anyMatch, caseSensitive);
		return this.findIndexBy(function( o )
		{
			return o && value.test(o[property]);
		}, null, start);
	},

	/**
	 * 通过自定义函数来获取索引
	 * 
	 * @param {} fn
	 * @param {} scope
	 * @param {} start 开始的索引
	 * @return {}
	 */
	findIndexBy: function( fn, scope, start )
	{
		var k = this.keys, it = this.items;
		for( var i = (start || 0), len = it.length; i < len; i++ )
		{
			if( fn.call(scope || this, it[i], k[i]) )
			{
				return i;
			}
		}
		return -1;
	},

	/**
	 * 看value是否是一个正则表达式，若是，就直接返回，若不是，就包装成正则返回
	 * 
	 * @param {} value 数组，或是正则
	 * @param {} anyMatch 是否匹配所有，要不就匹配开头的
	 * @param {} caseSensitive 大小写是否敏感
	 * @param {} exactMatch 是否为精确匹配
	 * @return {}
	 */
	createValueMatcher: function( value, anyMatch, caseSensitive, exactMatch )
	{
		if( !value.exec )
		{ // not a regex
			var er = Ext.escapeRe;
			value = String(value);

			if( anyMatch === true )
			{
				value = er(value);
			}
			else
			{
				value = '^' + er(value);
				if( exactMatch === true )
				{
					value += '$';
				}
			}
			value = new RegExp(value, caseSensitive ? '' : 'i');
		}
		return value;
	},

	/**
	 * 一个浅拷贝
	 * 
	 * @return {MixedCollection}
	 */
	clone: function()
	{
		var r = new Ext.util.MixedCollection();
		var k = this.keys, it = this.items;
		for( var i = 0, len = it.length; i < len; i++ )
		{
			r.add(k[i], it[i]);
		}
		r.getKey = this.getKey;
		return r;
	}
});
Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
